const {src,dest,series,parallel,watch} = require("gulp")
//下面的插件用到的时候会自动加载，所以可以用Plugins.xxx的形式简写
//require("gulp-load-plugins")是个方法，需要的是其返回值，返回一个对象，
//对象中的方法会动态自动加载
const Plugins = require("gulp-load-plugins")()
// //gulp-sass依赖sass
// const sass = require("gulp-sass")(require("sass"))
// //gulp-babel是ES6+语法转换的模块，它依赖@babel/core,并且最好安装预设（预设是插件的集合）模块@babel/preset-env
// const bable = require("gulp-babel")
// //html模板套用
// const swig = require("gulp-swig")
// //npm i gulp-imagemin@7.1.0,安装最新版本的话是ES module,用不了
// const imagemin = require("gulp-imagemin")
//安装del，清除模块
const del = require("del")
//服务相关模块
const browserSync = require("browser-sync")
//创建一个服务
const bs = browserSync.create()
//模板对应字段
const data = {
    pkg:require("./package.json"),
    date:new Date()
}

//css,js,html文件需要借助中间目录release完成编译、压缩等功能，然后再把最终文件输出到的dest目录中,所以需要把输出目录从dest\\src改为release\\src

//为什么新创建的scss文件以_开头则不会被打包，但可以通过引用该文件间接打包，引用时文件名没有_
//没有_的sass文件会单独打包
const style = () => {
    //{base:"src"}这个是源路径，不是目标路径
    return src("src/assets/styles/*.scss",{base:"src"})
            //编译scss文件为css，outputStyle:"expanded"表示是展开的样式，
            .pipe(Plugins.sass(require("sass"))({outputStyle:"expanded"}))
            //确定输出目录dest/src
            .pipe(dest("release\\src"))
            //重载浏览器，更新变化，{stream:true}属性让更新以流的形式，
            //否则pipe不支持
            .pipe(bs.reload({stream:true}))
}
//编译脚本
const script = () => {
    return src("src/assets/scripts/*.js",{base:"src"})
            //引用编译JS的预设，可以转换ES6+为ES5
            .pipe(Plugins.babel({presets:['@babel/preset-env']}))
            .pipe(dest("release\\src"))
            .pipe(bs.reload({stream:true}))
}
//编译html
const page = () => {
    //**表示匹配所有子目录
    return src("src/**/*.html",{base:"src"})
            //使用swig可以替换html中的模板字符
            //defaults: { cache: false }属性可以控制swig是否缓存，
            //默认是true，会导致部分模板字符串修改不生效
            .pipe(Plugins.swig({data,defaults: { cache: false }}))
            .pipe(dest("release\\src"))
            .pipe(bs.reload({stream:true}))
}
//编译图片
const image = () => {
    return src("src/assets/images/**",{base:"src"})
            //无损压缩图片文件
            .pipe(Plugins.imagemin())
            .pipe(dest("dest\\src"))
}
//编译font
const font = () => {
    return src("src/assets/fonts/**",{base:"src"})
            //gulp-imagemin可以压缩fonts中的图片文件，其他的文件无变化复制
            .pipe(Plugins.imagemin())
            .pipe(dest("dest\\src"))
}
//编译其他文件
const extra = () => {
    return src("public/**",{base:"public"})
            //修改目标目录，把其他静态文件都放到该目录中
            .pipe(dest("dest\\public"))
}
//清除之前的目标目录
const clean = () => {
    //del返回的是一个Promise，所以可以作为函数的返回值,每次构建都要先删除目标和中间目录
    return del(["dest","release"])
}
//服务任务
const serve = () => {
    //gulp的watch方法可以通过指定的监听文件变化，触发相应的任务
    //任务触发后会改变dest目录的文件使之变化，触发下面dest目录的热更新，
    //从而实现在开发目录实现热更新
    watch("src/assets/styles/*.scss",style)
    watch("src/assets/scripts/*.js",script)
    watch("src/**/*.html",page)
    //静态资源不需要一直监听，在生产打包的时候执行任务即可，
    //如果有改变想看效果可以在init中的files属性中添加相应目录路径
    // watch("src/assets/images/**",image)
    // watch("src/assets/fonts/**",font)
    // watch("public/**",extra)

    //当静态资源发生改变时，统一重新加载浏览器即可
    //在index.html中加入<img src="assets/fonts/test.svg" alt="{{ pkg.name }}'s logo">，并且创建test.svg测试有效
    watch([
        "src/assets/images/**",
        "src/assets/fonts/**",
        "public/**"
    ],bs.reload)//bs.reload这里不能是bs.reload()的形式

    //初始化服务，配置暂时重启生效
    bs.init({
        //右上角的提示
        notify:false,
        //端口
        port:2080,
        //是否新打开窗口
        open:true,
        //实时监听文件变化（热更新）（监听的是目标目录变化，修改本地src目录不会触发监听）
        //由于gulp的watch方法监听了所有的文件变化，所以bs中的监听就可以去掉了，但是要在watch触发的任务中加上bs.reload({stream:true}),更新浏览器
        // files:["dest/**"],
        //需要忽略的文件
        watchOptions: {
            ignoreInitial: true,
            ignored: 'release/**/*.md'
        },
        server:{
            //设置基础目录
            //baseDir属性可以是字符串，也可以是数组，
            //是数组的时候会按顺序依次查找目录（目录都是相对gulpfile文件的路径），
            //即dest/src目录不存在，就去src中找，直到找完。
            //目录中的内容在文件中被引用的路径是相对于列表中的目录的，
            //如：index.html中想引用public中的favicon.ico,则直接写favicon.ico即可，因为favicon.ico的路径就是public/favicon.ico。
            //如果是public/xxx/favicon.ico,则index.html中要写成xxx/favicon.ico
            //最后，服务启动目录从dest/src改成release/src
            baseDir:["release/src","src","public"],
            //改变原有路径
            routes:{
                //key是需要匹配的url，value是相对于gulpfile文件的相对路径
                "/node_modules":"./node_modules",
                // "/public":"public"
            }
        }
    })
}
//路径整合
const useref = () => {
    /**
     * html中以下面这种build/endbuild的构建注释形式包裹的引用标签，如：link,script等。
     * 通过gulp-useref模块可以使之再打包之后，不引用源资源的地址，而是根据build的配置生成新的资源和引用。
     * 从而解决打包后，找不到资源的问题。
        <!-- build:css assets/styles/vendor.css -->
        <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
        <!-- endbuild -->
     */
    //最后，修改目录，使文件从release到dest
    return src("release/src/**/*.html",{base:"release/src"})
            //searchPath:["dest/src","."]的意思是先到dest/src路径下找被包裹的引用路径，如：href中的源文件路径，没有则继续向下找，“.”代表gulpfile所在目录。
            //最后这里的路径也要修改把dest改成release
            .pipe(Plugins.useref({searchPath:["release/src","."]}))
            //在资源整合完后，准备输出之前，可以进行JS,CSS，HTML的压缩操作
            //如果是JS文件就执行terser()压缩JS，terser兼容了uglify可以替代
            // .pipe(Plugins.if(/\.js$/,Plugins.uglify()))
            .pipe(Plugins.if(/\.js$/,Plugins.terser()))
            .pipe(Plugins.if(/\.css$/,Plugins.cleanCss()))
            //压缩html文件需要额外配置
            .pipe(Plugins.if(/\.html$/,Plugins.htmlmin({
                collapseWhitespace:true,
                //压缩行内的CSS和JS,默认是false
                minifyCSS:true,
                minifyJS:true,
            })))
            //输出目录在build注释标签中已经配置好了，当前的实际情况就是dest/src中
            //在同一个目录下同时读和写会有问题，同一个文件不能同时进行
            //所以要创建一个新的临时目录存放处理后的文件
            // .pipe(dest("dest/src"))
            .pipe(dest("dest/src"))
}
// 创建多任务series串行，parallel并行
//编译
const compile = parallel(style,script,page)
//上线打包
//清除目录需要串行,生产打包前删除即可,image,font,extra上线时打包即可
//useref由于需要前置的release目录，但是可以和其他静态资源并行操作，所以把它放在compile后面即可
const build = 
    series(
        clean,
        parallel(
            series(compile,useref),
            image,
            font,
            extra
        )
    )
//开发配置，需要serve
const develop = series(compile,serve)
//最后不对外暴露不必要的命令
module.exports = {
    develop,
    // compile,
    build,
    // useref,
    // serve,
    clean,
    // style,
    // script,
    // page,
    // image,
    // font,
    // extra,
}

//可以将上述命令放入package.json中，方便开发人员调用,如：npm run clean
// "scripts": {
//     "clean": "gulp clean",
//     "build": "gulp build",
//     "develop": "gulp develop",
// },
//可以将release目录和dest目录，添加到.gitignore中，避免上传到git仓库中